package com.urick.loadbanlance.dubbo;

import cn.hutool.core.lang.Console;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class LoadBanlanceTester {
    static List<ServerInstance> serverInstanceList = new ArrayList<>(16);
    static String uid = StrUtil.uuid();
    static int totalWeight = 0;

    @BeforeClass
    public static void init() {
        Console.log("--------------init server instance list------------------");
        for (int i = 0; i < 4; i++) {
            int ipSuffix = i + 1;
            ServerInstance serverInstance = new ServerInstance();
            serverInstance.setUid(uid);
            serverInstance.setWeight(i + 1);
            serverInstance.setIp("127.0.0." + ipSuffix);
            serverInstance.setPort(RandomUtil.randomInt(3000, 65536));
            serverInstance.setAvailable(RandomUtil.randomBoolean());
//            serverInstance.setAvailable(Boolean.TRUE);
            serverInstance.setStartTime(System.currentTimeMillis());
//            serverInstance.setStartTime(1639407383930L);
            serverInstance.setWarmup(60 * 1000);
            serverInstanceList.add(serverInstance);
            Console.log(serverInstance);
            totalWeight += serverInstance.getWeight();
        }
        Console.log("--------------init server instance list------------------");
    }

    @Test
    public void randomTester() {
        int threadSize = 50000;
        CountDownLatch latch = new CountDownLatch(threadSize);
        for (int i = 0; i < threadSize; i++) {
            new Thread(() -> {
                ILoadBalance loadBalance = new RandomLoadBalance();
                ServerInstance server = loadBalance.getServer(serverInstanceList);
                server.getCount().incrementAndGet();
//                Console.error("{}--->{}", Thread.currentThread().getName(), server);
                try {
                    TimeUnit.MICROSECONDS.sleep(RandomUtil.randomInt(0, 200));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();

            }, "th-exe-req-" + i).start();
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Console.log("--------------loadbanlance server instance list------------------");
            serverInstanceList.forEach(v -> Console.log("uid:{}--weight:{}--expectRatio:{}%--actualRatio:{}%", v.getUid(), v.getWeight(), NumberUtil.div(v.getWeight(), totalWeight, 3), NumberUtil.div(v.getCount().get(), threadSize, 3)));
            Console.log("--------------loadbanlance server instance list------------------");
        }
    }

    @Test
    public void roundRobinRandomTester() {
        int threadSize = 10;
        CountDownLatch latch = new CountDownLatch(threadSize);
        for (int i = 0; i < threadSize; i++) {
            new Thread(() -> {
                ILoadBalance loadBalance = new RoundRobinLoadBalance();
                ServerInstance server = loadBalance.getServer(serverInstanceList);
                server.getCount().incrementAndGet();
                Console.error("{}--->{}", Thread.currentThread().getName(), server);
                try {
                    TimeUnit.MICROSECONDS.sleep(RandomUtil.randomInt(0, 200));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();

            }, "th-exe-req-" + i).start();
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Console.log("--------------loadbanlance server instance list------------------");
            serverInstanceList.forEach(v -> Console.log("uid:{}--weight:{}--expectRatio:{}%--actualRatio:{}%", v.getUid(), v.getWeight(), NumberUtil.div(v.getWeight(), totalWeight, 3), NumberUtil.div(v.getCount().get(), threadSize, 3)));
            Console.log("--------------loadbanlance server instance list------------------");
        }
    }


    @Test
    public void consistentHashTester() {
        int threadSize = 10;
                ILoadBalance loadBalance = new ConsistentHashTest();
        CountDownLatch latch = new CountDownLatch(threadSize);
        for (int i = 0; i < threadSize; i++) {
            new Thread(() -> {
                ServerInstance server = loadBalance.getServer(serverInstanceList);
                server.getCount().incrementAndGet();
                Console.error("{}--->{}", Thread.currentThread().getName(), server);
                try {
                    TimeUnit.MICROSECONDS.sleep(RandomUtil.randomInt(0, 200));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();

            }, "th-exe-req-" + i).start();
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Console.log("--------------loadbanlance server instance list------------------");
            serverInstanceList.forEach(v -> Console.log("uid:{}--weight:{}--expectRatio:{}%--actualRatio:{}%", v.getUid(), v.getWeight(), NumberUtil.div(v.getWeight(), totalWeight, 3), NumberUtil.div(v.getCount().get(), threadSize, 3)));
            Console.log("--------------loadbanlance server instance list------------------");
        }
    }

    static class ConsistentHashTest extends ConsistentHashLoadBalance{
        @Override
        protected String getMockSetArgument() {
            return "ConsistentHashTest";
        }
    }
}
